import React, { useState, useEffect, useCallback, useRef } from 'react';
// FIX: Import AiPromptsChatHistory
import { GameData, GameTurn, AppScreen, GameSettings, AiCallMode, TxtExportType, JsonPatchOperation, GuidingAiPlan, CalculationDebugInfo, MacroFunction, DynamicGameState, MacroScope, TurnUpdate, FunctionCallingTurnResponse, AiPrompts, AiPromptsChatHistory } from '../types';
import {
  generateImage,
  getModelAndChannel,
  generateHybridStructuredData,
  generateGuidingAiPlan,
  executeMacroFunctions,
  generateNarrativeForHybridMode,
  generateTurnWithFunctionCalling,
  ParseError,
  formatMacrosForToolCalling,
  formatFunctionsForPrompt,
  generateTurnSingleCall,
  generateNarrativeAndKeyInfo,
  updateStatusPanel,
  generateNarrativeOnly,
  extractKeyInfo,
} from '../services/apiService';
import { saveGame, exportGame, recalculateAggregatedKeyInfo, NATIVE_FUNCTIONS_DOCUMENTATION, MACRO_TUTORIAL_CONTENT, saveSettings } from '../services/gameSaveService';
import LoadingSpinner from './LoadingSpinner';
import {
  HYBRID_NARRATIVE_STREAM_TEMPLATE,
  NARRATIVE_AI_WITH_CALCS_TEMPLATE,
  TOOL_CALLING_TEMPLATE,
  CALCULATION_HELPER_AI_TEMPLATE,
  HYBRID_STRUCTURED_DATA_TEMPLATE,
} from '../constants';
import useIsLargeScreen from '../hooks/useIsLargeScreen';
import { applyJsonPatch } from '../utils/jsonPatch';
import { ChevronDoubleLeftIcon, ChevronDoubleRightIcon } from './icons';

import GameHeader from './game/GameHeader';
import LeftPanel from './game/LeftPanel';
import CenterPanel from './game/CenterPanel';
import RightPanel from './game/RightPanel';
import ManualParseModal from './game/modals/ManualParseModal';
import RulebookModal from './game/modals/RulebookModal';
import AiConfigModal from './game/modals/AiConfigModal';
import HelpModal from './game/modals/HelpModal';
import ExportTxtModal from './game/modals/ExportTxtModal';
import ConsolidateKeyInfoModal from './game/modals/ConsolidateKeyInfoModal';
import CalculationDebugModal from './game/modals/CalculationDebugModal';
import Modal from './Modal';

interface GameScreenProps {
  initialGameData: GameData;
  settings: GameSettings;
  setCurrentScreen: (screen: AppScreen) => void;
  showToast: (message: string, type?: 'success' | 'error') => void;
  onSettingsChange: (newSettings: GameSettings) => void;
  onGoToSettings: () => void;
}

const formatGameHistoryForPrompt = (history: GameTurn[]): string => history.map(turn => {
  let action = turn.customPlayerInput ? `玩家自定义: ${turn.customPlayerInput}` : (turn.chosenOptionIndex !== undefined ? `玩家选择: ${turn.options[turn.chosenOptionIndex]}` : "AI推进");
  return `行动: ${action}\n叙述: ${turn.narrative}\n---`;
}).join('\n');


const GameScreen: React.FC<GameScreenProps> = ({ initialGameData, settings, setCurrentScreen, showToast, onSettingsChange, onGoToSettings }) => {
  const [gameData, setGameData] = useState<GameData>(initialGameData);
  const [isLoading, setIsLoading] = useState(false);
  const [currentAiTask, setCurrentAiTask] = useState<string | null>(null);
  const [currentImage, setCurrentImage] = useState<string | null>(null);
  const [isImageLoading, setIsImageLoading] = useState(false);
  const [customAction, setCustomAction] = useState("");

  // Modal States
  const [showAiContextModal, setShowAiContextModal] = useState(false);
  const [showEditPromptsModal, setShowEditPromptsModal] = useState(false);
  const [showEditRulebookModal, setShowEditRulebookModal] = useState(false);
  const [showExportTxtModal, setShowExportTxtModal] = useState(false);
  const [isConsolidateModalOpen, setIsConsolidateModalOpen] = useState(false);
  const [isHelpModalOpen, setIsHelpModalOpen] = useState(false);
  const [manualParseInfo, setManualParseInfo] = useState<{ isOpen: boolean; rawText: string; onContinue: (parsedData: any) => void; onCancel: () => void; }>({ isOpen: false, rawText: '', onContinue: () => { }, onCancel: () => { } });
  const [calculationDebugInfo, setCalculationDebugInfo] = useState<CalculationDebugInfo | null>(null);
  const [isFunctionCallDebugModalOpen, setIsFunctionCallDebugModalOpen] = useState(false);

  // Editable states for modals
  const [editablePrompts, setEditablePrompts] = useState<AiPrompts>({ ...initialGameData.aiPrompts });
  const [editableMacros, setEditableMacros] = useState<MacroFunction[]>(initialGameData.macroFunctions ? JSON.parse(JSON.stringify(initialGameData.macroFunctions)) : []);
  // FIX: Add state for editableChatHistory
  const [editableChatHistory, setEditableChatHistory] = useState<AiPromptsChatHistory>(initialGameData.aiPromptsChatHistory ? JSON.parse(JSON.stringify(initialGameData.aiPromptsChatHistory)) : {});
  const [editableRulebook, setEditableRulebook] = useState({ ...initialGameData.rulebook });
  const [consolidationSummary, setConsolidationSummary] = useState('');
  const [helpModalContent, setHelpModalContent] = useState<{ title: string, content: any }>({ title: '', content: '' });
  
  const [changedPaths, setChangedPaths] = useState<Set<string>>(new Set());

  // Panel resizing state
  const isLargeScreen = useIsLargeScreen();
  const [isLeftPanelOpen, setIsLeftPanelOpen] = useState(true);
  const [isRightPanelOpen, setIsRightPanelOpen] = useState(true);
  const [leftPanelWidth, setLeftPanelWidth] = useState(25); // Percentage
  const [rightPanelWidth, setRightPanelWidth] = useState(25); // Percentage
  const [isResizing, setIsResizing] = useState<'left' | 'right' | null>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const mainContentRef = useRef<HTMLDivElement>(null);

  const currentTurnIndex = gameData.gameHistory.length - 1;
  const currentTurn = gameData.gameHistory[currentTurnIndex];

  const processAndEmbedNarrativeFunctions = useCallback(async (
    narrative: string,
    macros: MacroFunction[],
    currentState: DynamicGameState
  ): Promise<string> => {
      const narrativeMacros = macros.filter(m => m.scopes.includes(MacroScope.Narrative));
      if (narrativeMacros.length === 0) return narrative;
  
      const funcCallRegex = /(\w+)\s*\(([^)]*)\)/g;
      
      const matches = [...narrative.matchAll(funcCallRegex)];
      if (matches.length === 0) return narrative;

      let processedNarrative = narrative;
  
      for (const match of matches) {
          const fullMatch = match[0];
          const funcName = match[1];
          const argsStr = match[2];
  
          const definition = narrativeMacros.find(m => m.name === funcName);
          if (!definition) continue;
  
          const argsList: string[] = [];
          if (argsStr) {
            const argRegex = /(?:[^,"]+|"[^"]*")+/g;
            const matchedArgs = argsStr.match(argRegex);
            if(matchedArgs) {
              argsList.push(...matchedArgs.map(arg => arg.trim()));
            }
          }
          
          const argsObj: Record<string, any> = {};
          definition.parameters.forEach((param, i) => {
              if (argsList[i] !== undefined) {
                  let val = argsList[i];
                  try {
                      argsObj[param.name] = JSON.parse(val);
                  } catch (e) {
                      if (val.startsWith('"') && val.endsWith('"')) {
                          argsObj[param.name] = val.slice(1, -1);
                      } else {
                          argsObj[param.name] = val;
                      }
                  }
              }
          });
          
          const callToExecute = {
              name: funcName,
              args: argsObj,
          };
  
          const { individualResults } = await executeMacroFunctions(
              [callToExecute],
              narrativeMacros,
              currentState,
              false
          );
          
          const resultText = individualResults[0]?.result.replace('函数执行结果:\n', '').trim() || fullMatch;
          
          processedNarrative = processedNarrative.replace(fullMatch, resultText);
      }
      
      return processedNarrative;
  }, []);


  // Resizing logic effect
  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      if (!isResizing || !containerRef.current) return;
      const containerRect = containerRef.current.getBoundingClientRect();
      const containerWidth = containerRect.width;
      let newWidth = 0;
      if (isResizing === 'left') {
        newWidth = ((e.clientX - containerRect.left) / containerWidth) * 100;
        if (newWidth > 15 && newWidth < 50) setLeftPanelWidth(newWidth);
      } else { // 'right'
        newWidth = ((containerRect.right - e.clientX) / containerWidth) * 100;
        if (newWidth > 15 && newWidth < 50) setRightPanelWidth(newWidth);
      }
    };
    const handleMouseUp = () => {
      setIsResizing(null);
      document.body.style.cursor = 'default';
      document.body.style.userSelect = 'auto';
    };
    if (isResizing) {
      document.body.style.cursor = 'col-resize';
      document.body.style.userSelect = 'none';
      window.addEventListener('mousemove', handleMouseMove);
      window.addEventListener('mouseup', handleMouseUp);
    }
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isResizing]);

  const handleMouseDown = (e: React.MouseEvent, panel: 'left' | 'right') => {
    e.preventDefault();
    setIsResizing(panel);
  };

  // Sync editable data on game data change
  useEffect(() => {
    setEditablePrompts({ ...gameData.aiPrompts });
    setEditableRulebook({ ...gameData.rulebook });
    setEditableMacros(gameData.macroFunctions ? JSON.parse(JSON.stringify(gameData.macroFunctions)) : []);
  }, [gameData.aiPrompts, gameData.rulebook, gameData.macroFunctions]);

  // Scroll to bottom on new narrative
  useEffect(() => {
    if (mainContentRef.current) {
      mainContentRef.current.scrollTop = mainContentRef.current.scrollHeight;
    }
  }, [gameData.gameHistory, currentTurn?.narrative]);

  // Image generation effect
  useEffect(() => {
    if (currentTurn?.imageUrl) {
      setCurrentImage(currentTurn.imageUrl);
      setIsImageLoading(false);
    } else if (currentTurn?.imageQuery) {
      const { model: imageModel, channel: imageChannel } = getModelAndChannel(settings, settings.defaultNarrativeImageModelId);
      if (imageModel && imageChannel && imageChannel.baseUrl && imageChannel.apiKey) {
        setIsImageLoading(true);
        setCurrentAiTask("图片生成中...");
        generateImage(currentTurn.imageQuery, imageChannel, imageModel, settings.corsProxyUrl, settings.requestTimeout)
          .then(url => {
            setCurrentImage(url);
            setGameData(prevData => {
              const newHistory = [...prevData.gameHistory];
              if (newHistory[currentTurnIndex]) newHistory[currentTurnIndex].imageUrl = url;
              return { ...prevData, gameHistory: newHistory };
            });
          })
          .catch(err => {
            showToast(`图片生成失败: ${err.message}`, "error");
            setCurrentImage(`https://via.placeholder.com/600x400.png?text=Error+Loading+Image`);
          })
          .finally(() => {
            setIsImageLoading(false);
            setCurrentAiTask(null);
          });
      } else {
        setCurrentImage(null);
        setIsImageLoading(false);
        if (currentTurn?.imageQuery && settings.defaultNarrativeImageModelId) {
          showToast("默认叙事图片模型或其API渠道未正确配置。", "error");
          setCurrentImage(`https://via.placeholder.com/600x400.png?text=Image+API+Misconfigured`);
        } else if (currentTurn?.imageQuery && !settings.defaultNarrativeImageModelId) {
          setCurrentImage(`https://via.placeholder.com/600x400.png?text=No+Default+Image+Model`);
        }
      }
    } else {
      setCurrentImage(null);
      setIsImageLoading(false);
    }
  }, [currentTurn?.imageQuery, currentTurn?.imageUrl, settings, showToast, currentTurnIndex]);

  const handleSaveGame = useCallback(async () => {
    try {
      await saveGame(gameData);
      showToast("游戏已保存！", "success");
    } catch (e) {
      showToast(`保存失败: ${e instanceof Error ? e.message : '未知错误'}`, "error");
      console.error("Save game failed:", e);
    }
  }, [gameData, showToast]);

  const processTurn = useCallback(async (playerActionText: string, isCustomAction: boolean = false) => {
    const { model: textModel, channel: textChannel } = getModelAndChannel(settings, settings.defaultTextModelId);
    if (!textModel || !textChannel || !textChannel.baseUrl || !textChannel.apiKey) {
      showToast("默认文本模型或其API渠道未正确配置。请前往设置。", "error");
      onGoToSettings();
      return;
    }
    setIsLoading(true);
    setCurrentImage(null);
    setChangedPaths(new Set());

    const onCancel = () => {
      setIsLoading(false);
      setCurrentAiTask(null);
      setManualParseInfo(prev => ({ ...prev, isOpen: false }));
    };

    const tempTurnId = `temp-turn-${Date.now()}`;
    const previousHistory = [...gameData.gameHistory];
    const statusPanelOfPreviousTurn = previousHistory.length > 0 ? previousHistory[previousHistory.length - 1].turnStateSnapshot : {};
    const statusStringForPrompts = JSON.stringify(statusPanelOfPreviousTurn, null, 2);
    const aggregatedKeyInfoForAi = recalculateAggregatedKeyInfo(previousHistory);

    const setupPlaceholderTurn = () => {
        setGameData(prevData => {
            let updatedHistorySnapshot = [...prevData.gameHistory];
            if (updatedHistorySnapshot.length > 0 && playerActionText !== "开始游戏" && !isCustomAction) {
                const lastTurnToUpdate = updatedHistorySnapshot[updatedHistorySnapshot.length - 1];
                const choiceIndex = lastTurnToUpdate.options.findIndex(opt => opt === playerActionText);
                if (choiceIndex !== -1) lastTurnToUpdate.chosenOptionIndex = choiceIndex;
            }
            const placeholderTurn: GameTurn = { id: tempTurnId, narrative: '', options: [], turnStateSnapshot: statusPanelOfPreviousTurn, turnKeyInfo: '', ...(isCustomAction && { customPlayerInput: playerActionText }) };
            return { ...prevData, gameHistory: [...updatedHistorySnapshot, placeholderTurn] };
        });
    };

    const handleApiResponse = (
        narrative: string,
        options: string[],
        imageQuery: string | undefined,
        newStatusPanel: DynamicGameState,
        keyInfoUpdate: string,
        promptsSentToAi: GameTurn['promptsSentToAi']
    ) => {
        setChangedPaths(new Set()); // Reset highlights
        setGameData(prevData => {
            const newHistory = [...prevData.gameHistory];
            const finalTurnIndex = newHistory.findIndex(t => t.id === tempTurnId);
            if (finalTurnIndex > -1) {
                const finalTurn: GameTurn = {
                    ...newHistory[finalTurnIndex],
                    id: `turnuuid-${Date.now()}`,
                    narrative,
                    options,
                    imageQuery,
                    turnStateSnapshot: newStatusPanel,
                    turnKeyInfo: keyInfoUpdate,
                    promptsSentToAi,
                };
                newHistory[finalTurnIndex] = finalTurn;
            }
            const finalData = { ...prevData, gameHistory: newHistory, aggregatedKeyInfo: recalculateAggregatedKeyInfo(newHistory) };
            saveGame(finalData).catch(e => showToast(`自动保存失败: ${e instanceof Error ? e.message : '未知错误'}`, "error"));
            return finalData;
        });
        setCustomAction("");
        setIsLoading(false);
        setCurrentAiTask(null);
    };

    const handleError = (e: unknown) => {
        const errorMessage = e instanceof Error ? e.message : "未知错误";
        showToast(`处理回合时出错: ${errorMessage}`, "error");
        if (e instanceof ParseError) {
             console.error("Parse Error Raw Text:", e.rawText);
             setManualParseInfo({
                 isOpen: true,
                 rawText: e.rawText,
                 onContinue: (parsedData) => {
                     console.log("手动解析的数据:", parsedData);
                     showToast("手动解析成功，但此模式不支持自动继续，请重试操作。", "error");
                     onCancel();
                 },
                 onCancel,
             });
        } else {
            setGameData(prev => ({ ...prev, gameHistory: prev.gameHistory.filter(t => t.id !== tempTurnId) }));
            onCancel();
        }
    };
    
    const executeFunctionCallingStep = async () => {
        try {
            const onUpdateCallback = (update: TurnUpdate) => {
                if (update.type === 'task') {
                    setCurrentAiTask(update.content);
                } else if (update.type === 'function_result') {
                    showToast(update.content, 'success');
                }
            };

            setCurrentAiTask("AI 思考中 (函数调用模式)...");
            const aggregatedKeyInfoForAi = recalculateAggregatedKeyInfo(previousHistory);
            
            // --- CONSTRUCT FULL PROMPT FOR LOGGING ---
            const statusStringForLogging = JSON.stringify(statusPanelOfPreviousTurn, null, 2);
            const historyStringForLogging = formatGameHistoryForPrompt(previousHistory.slice(-settings.maxHistoryLengthToDisplay));
            const narrativePrinciples = gameData.aiPrompts.functionCallingNarrativePrompt || gameData.aiPrompts.scenarioGenerationPrompt || "请生成有趣且连贯的叙事。";
            
            const systemPrompt = gameData.aiPrompts.toolCallingSystemPrompt || TOOL_CALLING_TEMPLATE;
            const systemPromptForDisplay = systemPrompt.replace('{narrative_principles}', narrativePrinciples);
            
            const userPromptContentForDisplay = `
游戏核心设定:
背景故事:
${gameData.rulebook.backgroundStory}
核心规则:
${gameData.rulebook.rules}

当前状态面板内容:
${statusStringForLogging}

当前累计关键信息:
${aggregatedKeyInfoForAi || "暂无关键信息。"}

最近的游戏历史:
${historyStringForLogging || "这是游戏的开始。"}

玩家在本回合的行动/选择是: "${playerActionText}"

请根据以上信息开始生成回合。
`;
            const toolsForDisplay = JSON.stringify(formatMacrosForToolCalling(gameData.macroFunctions), null, 2);
            const fullPromptForLogging = `### 系统指令 (System Prompt)\n${systemPromptForDisplay}\n\n### 用户输入 (User Input)\n${userPromptContentForDisplay}\n\n### 可用工具 (Tools)\n\`\`\`json\n${toolsForDisplay}\n\`\`\``;
            
            setGameData(prevData => {
                const newHistory = [...prevData.gameHistory];
                const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                if (turnToUpdate) {
                    turnToUpdate.promptsSentToAi = { functionCalling: fullPromptForLogging };
                }
                return { ...prevData, gameHistory: newHistory };
            });

            const { narrative, structuredData, functionCallHistory } = await generateTurnWithFunctionCalling(
                previousHistory, playerActionText, gameData.rulebook, gameData.aiPrompts, aggregatedKeyInfoForAi, settings.maxHistoryLengthToDisplay, textChannel, textModel, JSON.stringify(statusPanelOfPreviousTurn), gameData.macroFunctions, statusPanelOfPreviousTurn, onUpdateCallback, settings.corsProxyUrl, settings.requestTimeout, systemPrompt
            );

            onUpdateCallback({ type: 'task', content: '处理最终结果...'});
            let stateAfterFunctions = statusPanelOfPreviousTurn;
            const functionPatches = functionCallHistory
                ?.map(h => (h as any).patch) // A bit of a hack, assuming patch is there from our custom macros
                .filter(p => p)
                .flat() as JsonPatchOperation[] || [];
            
            if(functionPatches.length > 0){
                stateAfterFunctions = applyJsonPatch(stateAfterFunctions, functionPatches);
            }
            
            let allPatches: JsonPatchOperation[] = structuredData.statusPanelPatch || [];
            if (structuredData.function_calls && structuredData.function_calls.length > 0) {
                onUpdateCallback({ type: 'task', content: '执行最终状态函数...'});
                const { patches: funcPatches, individualResults } = await executeMacroFunctions(structuredData.function_calls, gameData.macroFunctions, stateAfterFunctions, true);
                allPatches.push(...funcPatches);
                individualResults.forEach(r => onUpdateCallback({ type: 'function_result', content: r.result }));
                 if (functionCallHistory) {
                  functionCallHistory.push(...individualResults);
                }
            }
            
            const finalPatches = [...functionPatches, ...allPatches];

            const pathsToHighlight = new Set<string>();
            finalPatches.forEach(op => {
                const parts = op.path.split('/').slice(1);
                let currentPath = '';
                for (const part of parts) { currentPath += '/' + part; pathsToHighlight.add(currentPath); }
            });
            setChangedPaths(pathsToHighlight);
            const newStatusPanel = applyJsonPatch(statusPanelOfPreviousTurn, finalPatches);

            handleApiResponse(
                narrative,
                structuredData.options,
                structuredData.imageQuery,
                newStatusPanel,
                structuredData.keyInfoUpdate,
                { functionCalling: fullPromptForLogging }
            );
            // This needs to be set after handleApiResponse for the debug modal to show correct data
            setGameData(prev => {
                const newHistory = [...prev.gameHistory];
                const lastTurn = newHistory[newHistory.length - 1];
                if(lastTurn) lastTurn.functionCallHistory = functionCallHistory;
                return {...prev, gameHistory: newHistory};
            });
            
        } catch (e) {
            handleError(e);
        }
    };


    const executeHybridStep = async () => {
      try {
        const { model: narrativeModel, channel: narrativeChannel } = getModelAndChannel(settings, settings.defaultHybridNarrativeModelId || settings.defaultTextModelId);
        const { model: statusModel, channel: statusChannel } = getModelAndChannel(settings, settings.defaultHybridStatusModelId || settings.defaultTextModelId);

        if (!narrativeModel || !narrativeChannel || !statusModel || !statusChannel) {
          throw new Error("混合模式所需的叙事或状态模型未正确配置。");
        }

        const generateNarrativeAndFinalize = async (basePromptTemplate: string, promptParams: Record<string, string>, stateBeforePatch: DynamicGameState) => {
            const narrativeFunctionList = formatFunctionsForPrompt(gameData.macroFunctions, MacroScope.Narrative);
            let narrativePromptForLogging = basePromptTemplate.replace('{functionList}', narrativeFunctionList);
            for (const key in promptParams) {
                narrativePromptForLogging = narrativePromptForLogging.replace(`{${key}}`, promptParams[key as keyof typeof promptParams]);
            }

            setGameData(prevData => {
                const newHistory = [...prevData.gameHistory];
                const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                if (turnToUpdate) {
                    turnToUpdate.promptsSentToAi = { ...turnToUpdate.promptsSentToAi, narrative: narrativePromptForLogging };
                }
                return { ...prevData, gameHistory: newHistory };
            });

            const narrativeGenPromise = generateNarrativeForHybridMode(narrativeChannel, narrativeModel, basePromptTemplate, promptParams, gameData.macroFunctions, settings.hybridModeStreamNarrative ?? true, settings.corsProxyUrl, settings.requestTimeout);

            let fullNarrative = "";
            if (settings.hybridModeStreamNarrative ?? true) {
                setCurrentAiTask("请求叙事AI生成最终场景...");
                const stream = await narrativeGenPromise as AsyncGenerator<string>;
                for await (const chunk of stream) {
                    fullNarrative += chunk;
                    setGameData(prevData => {
                        const newHistory = [...prevData.gameHistory];
                        const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                        if (turnToUpdate) turnToUpdate.narrative = fullNarrative;
                        return { ...prevData, gameHistory: newHistory };
                    });
                }
            } else {
                setCurrentAiTask("请求叙事AI生成最终场景...");
                fullNarrative = await narrativeGenPromise as string;
                setGameData(prevData => {
                    const newHistory = [...prevData.gameHistory];
                    const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                    if (turnToUpdate) turnToUpdate.narrative = fullNarrative;
                    return { ...prevData, gameHistory: newHistory };
                });
            }

            setCurrentAiTask("处理叙事内函数...");
            const narrativeWithResults = await processAndEmbedNarrativeFunctions(fullNarrative, gameData.macroFunctions, stateBeforePatch);
            setGameData(prevData => {
                const newHistory = [...prevData.gameHistory];
                const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                if (turnToUpdate) turnToUpdate.narrative = narrativeWithResults;
                return { ...prevData, gameHistory: newHistory };
            });

            setCurrentAiTask("AI 2: 生成游戏数据...");
            
            const statusFunctionList = formatFunctionsForPrompt(gameData.macroFunctions, MacroScope.Status);
            const statusSystemPrompt = gameData.aiPrompts.hybridStructuredDataSystemPrompt || HYBRID_STRUCTURED_DATA_TEMPLATE;
            const statusPromptForLogging = statusSystemPrompt
                .replace('{functionList}', statusFunctionList)
                .replace('{gameBackgroundStory}', gameData.rulebook.backgroundStory)
                .replace('{gameRules}', gameData.rulebook.rules)
                .replace('{statusPanelPrompt}', gameData.aiPrompts.statusPanelPrompt)
                .replace('{keyInfoExtractionPrompt}', gameData.aiPrompts.keyInfoExtractionPrompt)
                .replace('{playerAction}', playerActionText)
                .replace('{currentNarrative}', narrativeWithResults)
                .replace('{currentStatusPanelContentString}', JSON.stringify(stateBeforePatch, null, 2));

            setGameData(prevData => {
                const newHistory = [...prevData.gameHistory];
                const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                if (turnToUpdate) {
                    turnToUpdate.promptsSentToAi = {
                        ...turnToUpdate.promptsSentToAi,
                        structuredData: statusPromptForLogging
                    };
                }
                return { ...prevData, gameHistory: newHistory };
            });
            
            const structuredData = await generateHybridStructuredData(playerActionText, narrativeWithResults, gameData.aiPrompts, gameData.rulebook, statusChannel, statusModel, JSON.stringify(stateBeforePatch, null, 2), gameData.macroFunctions, settings.corsProxyUrl, settings.requestTimeout, statusSystemPrompt);
            
            let allPatches = structuredData.statusPanelPatch || [];

            if (structuredData.function_calls && structuredData.function_calls.length > 0) {
                setCurrentAiTask("执行状态更新函数...");
                const { patches: funcPatches } = await executeMacroFunctions(structuredData.function_calls, gameData.macroFunctions, stateBeforePatch, true);
                allPatches.push(...funcPatches);
            }
            
            const pathsToHighlight = new Set<string>();
            allPatches.forEach(op => {
                const parts = op.path.split('/').slice(1);
                let currentPath = '';
                for (const part of parts) {
                    currentPath += '/' + part;
                    pathsToHighlight.add(currentPath);
                }
            });
            setChangedPaths(pathsToHighlight);
            
            const newStatusPanel = applyJsonPatch(stateBeforePatch, allPatches);
            handleApiResponse(
                narrativeWithResults,
                structuredData.options,
                structuredData.imageQuery,
                newStatusPanel,
                structuredData.keyInfoUpdate,
                { narrative: narrativePromptForLogging, structuredData: statusPromptForLogging }
            );
        };

        const continueFullExecution = async (plan: GuidingAiPlan, calculationResults: string, macroPatches: JsonPatchOperation[]) => {
          const stateAfterMacro = applyJsonPatch(statusPanelOfPreviousTurn, macroPatches);
          const aggregatedKeyInfoForAi = recalculateAggregatedKeyInfo(previousHistory);

          const promptParams = {
              gameBackgroundStory: gameData.rulebook.backgroundStory,
              gameRules: gameData.rulebook.rules,
              short_prompt: plan.short_prompt,
              calculation_results: calculationResults,
              currentStatusPanelContentString: JSON.stringify(stateAfterMacro, null, 2),
              aggregatedKeyInfo: aggregatedKeyInfoForAi || "暂无关键信息。",
              maxHistoryLengthToDisplay: String(settings.maxHistoryLengthToDisplay),
              gameHistoryString: formatGameHistoryForPrompt(previousHistory.slice(-settings.maxHistoryLengthToDisplay)) || "这是游戏的开始。",
              playerAction: playerActionText
          };
          
          const narrativeSystemPrompt = gameData.aiPrompts.narrativeWithCalcsSystemPrompt || NARRATIVE_AI_WITH_CALCS_TEMPLATE;
          await generateNarrativeAndFinalize(narrativeSystemPrompt, promptParams, stateAfterMacro);
        };

        if (settings.aiCallMode === AiCallMode.Hybrid && settings.enableCalculationHelperAi) {
          const { model: planningModel, channel: planningChannel } = getModelAndChannel(settings, settings.defaultHybridPlanningModelId || settings.defaultTextModelId);
          if (!planningModel || !planningChannel) {
              throw new Error("混合模式所需的规划模型未正确配置。");
          }

          setCurrentAiTask("请求计算助手进行规划...");
          const aggregatedKeyInfoForAi = recalculateAggregatedKeyInfo(previousHistory);
          const statusStringForPrompts = JSON.stringify(statusPanelOfPreviousTurn, null, 2);
          
          const calcHelperSystemPrompt = gameData.aiPrompts.calculationHelperSystemPrompt || CALCULATION_HELPER_AI_TEMPLATE;  
          const calcHelperPromptForLogging = calcHelperSystemPrompt
            .replace('{functionList}', formatFunctionsForPrompt(gameData.macroFunctions, MacroScope.Planning))
            .replace('{gameBackgroundStory}', gameData.rulebook.backgroundStory)
            .replace('{gameRules}', gameData.rulebook.rules)
            .replace('{currentStatusPanelContentString}', statusStringForPrompts)
            .replace('{aggregatedKeyInfo}', aggregatedKeyInfoForAi || "暂无关键信息。")
            .replace('{maxHistoryLengthToDisplay}', String(settings.maxHistoryLengthToDisplay))
            .replace('{gameHistoryString}', formatGameHistoryForPrompt(previousHistory.slice(-settings.maxHistoryLengthToDisplay)) || "这是游戏的开始。")
            .replace('{playerAction}', playerActionText);

          setGameData(prevData => {
            const newHistory = [...prevData.gameHistory];
            const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
            if (turnToUpdate) {
                turnToUpdate.promptsSentToAi = { calculationHelper: calcHelperPromptForLogging };
            }
            return { ...prevData, gameHistory: newHistory };
          });

          const plan = await generateGuidingAiPlan(previousHistory, playerActionText, gameData.rulebook, aggregatedKeyInfoForAi, settings.maxHistoryLengthToDisplay, planningChannel, planningModel, statusStringForPrompts, gameData.macroFunctions, settings.corsProxyUrl, settings.requestTimeout, calcHelperSystemPrompt);
          setCurrentAiTask("正在执行本地计算...");
          const { resultsString: calculationResults, patches: macroPatches } = await executeMacroFunctions(plan.function_calls, gameData.macroFunctions, statusPanelOfPreviousTurn);

          if (settings.showCalculationHelperAiPlan) {
            setCalculationDebugInfo({
              plan,
              results: calculationResults,
              onContinue: () => {
                setCalculationDebugInfo(null);
                continueFullExecution(plan, calculationResults, macroPatches);
              }
            });
          } else {
            await continueFullExecution(plan, calculationResults, macroPatches);
          }
        } else {
          const aggregatedKeyInfoForAi = recalculateAggregatedKeyInfo(previousHistory);
          const promptParams = {
              gameBackgroundStory: gameData.rulebook.backgroundStory,
              gameRules: gameData.rulebook.rules,
              scenarioGenerationPrompt: gameData.aiPrompts.scenarioGenerationPrompt,
              currentStatusPanelContentString: JSON.stringify(statusPanelOfPreviousTurn, null, 2),
              aggregatedKeyInfo: aggregatedKeyInfoForAi || "暂无关键信息。",
              maxHistoryLengthToDisplay: String(settings.maxHistoryLengthToDisplay),
              gameHistoryString: formatGameHistoryForPrompt(previousHistory.slice(-settings.maxHistoryLengthToDisplay)) || (previousHistory.length === 0 ? "这是游戏的开始。" : "无先前历史记录。"),
              playerAction: playerActionText,
          };

          await generateNarrativeAndFinalize(HYBRID_NARRATIVE_STREAM_TEMPLATE, promptParams, statusPanelOfPreviousTurn);
        }
      } catch (e) {
        handleError(e);
      }
    };
    
    const executeSingleCallStep = async () => {
        try {
            setCurrentAiTask("AI 正在整合思考...");
            const { response, fullPrompt } = await generateTurnSingleCall(previousHistory, playerActionText, gameData.rulebook, gameData.aiPrompts, aggregatedKeyInfoForAi, settings.maxHistoryLengthToDisplay, textChannel, textModel, statusStringForPrompts, settings.corsProxyUrl, settings.requestTimeout);
            handleApiResponse(
                response.scenario.narrative,
                response.scenario.options,
                response.scenario.imageQuery,
                response.statusPanelContent,
                response.keyInfoUpdate,
                { narrative: fullPrompt }
            );
        } catch (e) {
            handleError(e);
        }
    };
    
    const executeDoubleCallStep = async () => {
        try {
            setCurrentAiTask("AI 1/2: 生成叙事与关键信息...");
            const { response: narrativeResponse, fullPrompt: narrativePrompt } = await generateNarrativeAndKeyInfo(previousHistory, playerActionText, gameData.rulebook, gameData.aiPrompts, aggregatedKeyInfoForAi, settings.maxHistoryLengthToDisplay, textChannel, textModel, statusStringForPrompts, settings.corsProxyUrl, settings.requestTimeout);
            
            setGameData(prevData => {
                const newHistory = [...prevData.gameHistory];
                const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                if (turnToUpdate) turnToUpdate.narrative = narrativeResponse.narrative;
                return { ...prevData, gameHistory: newHistory };
            });

            setCurrentAiTask("AI 2/2: 更新游戏状态...");
            const { response: statusResponse, fullPrompt: statusPrompt } = await updateStatusPanel(playerActionText, narrativeResponse.narrative, gameData.rulebook, gameData.aiPrompts, textChannel, textModel, statusStringForPrompts, settings.corsProxyUrl, settings.requestTimeout);

            handleApiResponse(
                narrativeResponse.narrative,
                narrativeResponse.options,
                narrativeResponse.imageQuery,
                statusResponse,
                narrativeResponse.keyInfoUpdate,
                { narrative: narrativePrompt, structuredData: statusPrompt }
            );
        } catch (e) {
            handleError(e);
        }
    };

    const executeTripleCallStep = async () => {
        try {
            setCurrentAiTask("AI 1/3: 生成叙事...");
            const { response: narrativeResponse, fullPrompt: narrativePrompt } = await generateNarrativeOnly(previousHistory, playerActionText, gameData.rulebook, gameData.aiPrompts, aggregatedKeyInfoForAi, settings.maxHistoryLengthToDisplay, textChannel, textModel, statusStringForPrompts, settings.corsProxyUrl, settings.requestTimeout);

            setGameData(prevData => {
                const newHistory = [...prevData.gameHistory];
                const turnToUpdate = newHistory.find(t => t.id === tempTurnId);
                if (turnToUpdate) turnToUpdate.narrative = narrativeResponse.narrative;
                return { ...prevData, gameHistory: newHistory };
            });

            setCurrentAiTask("AI 2/3: 更新游戏状态...");
            const { response: statusResponse, fullPrompt: statusPrompt } = await updateStatusPanel(playerActionText, narrativeResponse.narrative, gameData.rulebook, gameData.aiPrompts, textChannel, textModel, statusStringForPrompts, settings.corsProxyUrl, settings.requestTimeout);

            setCurrentAiTask("AI 3/3: 提取关键信息...");
            const { response: keyInfoResponse, fullPrompt: keyInfoPrompt } = await extractKeyInfo(playerActionText, narrativeResponse.narrative, gameData.rulebook, gameData.aiPrompts, textChannel, textModel, statusStringForPrompts, settings.corsProxyUrl, settings.requestTimeout);

            handleApiResponse(
                narrativeResponse.narrative,
                narrativeResponse.options,
                narrativeResponse.imageQuery,
                statusResponse,
                keyInfoResponse,
                { narrative: narrativePrompt, structuredData: statusPrompt, calculationHelper: keyInfoPrompt }
            );
        } catch (e) {
            handleError(e);
        }
    };
    
    setupPlaceholderTurn();

    switch (settings.aiCallMode) {
      case AiCallMode.Hybrid:
        if (settings.enableFunctionCallingMode) {
          await executeFunctionCallingStep();
        } else {
          await executeHybridStep();
        }
        break;
      case AiCallMode.Single:
        await executeSingleCallStep();
        break;
      case AiCallMode.Double:
        await executeDoubleCallStep();
        break;
      case AiCallMode.Triple:
        await executeTripleCallStep();
        break;
      default:
        showToast(`模式 ${settings.aiCallMode} 不支持。`, "error");
        onCancel();
    }

  }, [gameData, settings, showToast, processAndEmbedNarrativeFunctions, onGoToSettings]);

  // Initial turn generation
  useEffect(() => {
    if (gameData.gameHistory.length === 0 && !isLoading) {
      const { model, channel } = getModelAndChannel(settings, settings.defaultTextModelId);
      if (model && channel && channel.baseUrl && channel.apiKey) {
        processTurn("开始游戏", false);
      } else if (!manualParseInfo.isOpen) {
        showToast("默认文本模型或API渠道未配置。请前往设置。", "error");
        setCurrentScreen(AppScreen.Settings);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // --- Event Handlers ---
  const handleOptionClick = (optionText: string, optionIndex: number) => {
    if (isLoading) return;
    const currentDisplayedTurn = gameData.gameHistory[currentTurnIndex];
    const actualOptionText = currentDisplayedTurn?.options[optionIndex] || optionText;
    processTurn(actualOptionText, false);
  };
  const handleCustomActionSubmit = () => {
    if (isLoading || !customAction.trim()) return;
    processTurn(customAction.trim(), true);
  };
  const handleExportGame = () => {
    exportGame(gameData);
    showToast("游戏存档已导出。", "success");
  };
  const handleExportTxt = (type: TxtExportType) => {
    let content = `游戏: ${gameData.rulebook.gameName}\n导出时间: ${new Date().toLocaleString('zh-CN')}\n\n`;
    const historyToExport = gameData.gameHistory;
    if (type === 'narrative') content += historyToExport.map((turn, index) => `--- 回合 ${index + 1} ---\n\n${turn.narrative}`).join('\n\n');
    else if (type === 'keyInfo') content += historyToExport.map((turn, index) => `--- 回合 ${index + 1} 关键信息 ---\n\n${turn.turnKeyInfo || '无'}`).join('\n\n');
    else content += historyToExport.map((turn, index) => `--- 回合 ${index + 1} ---\n\n[叙事]\n${turn.narrative}\n\n[关键信息]\n${turn.turnKeyInfo || '无'}`).join('\n\n');
    const filename = `${gameData.rulebook.gameName.replace(/[\s\\/:"*?<>|]+/g, '_')}_${type}.txt`;
    const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
    setShowExportTxtModal(false);
    showToast("文本文件已导出。", "success");
  };
  const handleExportStatusTxt = () => {
    if (!currentTurn || !currentTurn.turnStateSnapshot) {
      showToast("没有当前状态信息可导出。", "error");
      return;
    }
    let content = `游戏: ${gameData.rulebook.gameName}\n导出时间: ${new Date().toLocaleString('zh-CN')}\n\n--- 当前状态栏 (回合 ${gameData.gameHistory.length}) ---\n\n${JSON.stringify(currentTurn.turnStateSnapshot, null, 2)}`;
    const filename = `${gameData.rulebook.gameName.replace(/[\s\\/:"*?<>|]+/g, '_')}_status.txt`;
    const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
    setShowExportTxtModal(false);
    showToast("状态栏文件已导出。", "success");
  };
  const handleBacktrack = async () => {
    if (gameData.gameHistory.length <= 1) {
      showToast("无法回溯到初始状态之外。", "error");
      return;
    }
    setIsLoading(true);
    setCurrentAiTask("正在回溯...");
    const newHistory = gameData.gameHistory.slice(0, -1);
    const newAggregatedKeyInfo = recalculateAggregatedKeyInfo(newHistory);
    const lastValidTurn = newHistory[newHistory.length - 1];
    let actionToRestore = "";
    if (lastValidTurn) {
      if (lastValidTurn.customPlayerInput) actionToRestore = lastValidTurn.customPlayerInput;
      else if (lastValidTurn.chosenOptionIndex !== undefined && lastValidTurn.options[lastValidTurn.chosenOptionIndex]) actionToRestore = lastValidTurn.options[lastValidTurn.chosenOptionIndex];
    }
    setCurrentImage(lastValidTurn?.imageUrl || null);
    setChangedPaths(new Set());
    const updatedGameData = { ...gameData, gameHistory: newHistory, aggregatedKeyInfo: newAggregatedKeyInfo };
    setGameData(updatedGameData);
    await saveGame(updatedGameData);
    setCustomAction(actionToRestore);
    showToast("已回溯到上一选项。", "success");
    setIsLoading(false);
    setCurrentAiTask(null);
  };
  const handleConsolidateKeyInfo = () => {
    if (gameData.gameHistory.length < 1) {
      showToast("至少需要一个回合才能整合关键信息。", "error");
      return;
    }
    const summary = recalculateAggregatedKeyInfo(gameData.gameHistory);
    setConsolidationSummary(summary);
    setIsConsolidateModalOpen(true);
  };
  const confirmAndApplyConsolidation = async () => {
    setIsLoading(true);
    setCurrentAiTask("正在整合关键信息...");
    const newHistory = JSON.parse(JSON.stringify(gameData.gameHistory));
    for (let i = 0; i < newHistory.length; i++) newHistory[i].turnKeyInfo = '';
    if (newHistory.length > 0) newHistory[newHistory.length - 1].turnKeyInfo = consolidationSummary;
    const newAggregatedKeyInfo = recalculateAggregatedKeyInfo(newHistory);
    const updatedGameData = { ...gameData, gameHistory: newHistory, aggregatedKeyInfo: newAggregatedKeyInfo };
    setGameData(updatedGameData);
    await saveGame(updatedGameData);
    setIsConsolidateModalOpen(false);
    setConsolidationSummary('');
    showToast("关键信息已整合。", "success");
    setIsLoading(false);
    setCurrentAiTask(null);
  };
  const updateField = useCallback(async (path: string, value: any, skipSave: boolean = false) => {
    const updater = (prevData: GameData): GameData => {
      const keys = path.split('.');
      const newData = JSON.parse(JSON.stringify(prevData));
      let current = newData;
      for (let i = 0; i < keys.length - 1; i++) {
        if (!current[keys[i]] || typeof current[keys[i]] !== 'object') current[keys[i]] = {};
        current = current[keys[i]];
      }
      current[keys[keys.length - 1]] = value;
      return newData;
    };
    setGameData(prevData => {
      const newData = updater(prevData);
      if (!skipSave) saveGame(newData).catch(e => console.error("Failed to save after field update:", e));
      return newData;
    });
  }, []);
  const handleTurnKeyInfoSave = useCallback(async (turnIndex: number, newKeyInfo: string) => {
    const updater = (prevData: GameData): GameData => {
      const newHistory = [...prevData.gameHistory];
      if (newHistory[turnIndex]) newHistory[turnIndex].turnKeyInfo = newKeyInfo;
      return { ...prevData, gameHistory: newHistory, aggregatedKeyInfo: recalculateAggregatedKeyInfo(newHistory) };
    };
    setGameData(prevData => {
      const newData = updater(prevData);
      saveGame(newData).catch(e => console.error("Failed to save after key info update:", e));
      return newData;
    });
  }, []);
  const updateDynamicStateField = useCallback(async (turnIdx: number, fieldPath: string, value: any) => {
    const updater = (prevData: GameData): GameData => {
      const newHistory = JSON.parse(JSON.stringify(prevData.gameHistory));
      if (newHistory[turnIdx] && newHistory[turnIdx].turnStateSnapshot) {
        const pathParts = fieldPath.split('/').slice(1);
        let current = newHistory[turnIdx].turnStateSnapshot;
        for (let i = 0; i < pathParts.length - 1; i++) {
          const part = pathParts[i];
          if (current[part] === undefined) current[part] = {};
          current = current[part];
        }
        const finalPart = pathParts[pathParts.length - 1];
        if (Array.isArray(current) && !isNaN(parseInt(finalPart, 10))) current[parseInt(finalPart, 10)] = value;
        else current[finalPart] = value;
      }
      return { ...prevData, gameHistory: newHistory };
    };
    setGameData(prevData => {
      const newData = updater(prevData);
      saveGame(newData).catch(e => console.error("Failed to save after dynamic state update:", e));
      return newData;
    });
  }, []);
  const saveEditedPromptsAndMacros = async () => {
    // FIX: Include chat history when saving AI config
    const updatedGameData = { ...gameData, aiPrompts: { ...editablePrompts }, macroFunctions: [...editableMacros], aiPromptsChatHistory: { ...editableChatHistory } };
    setGameData(updatedGameData);
    await saveGame(updatedGameData);
    showToast("AI配置（提示词和宏函数）已更新并保存。", "success");
    setShowEditPromptsModal(false);
  };
  const saveEditedRulebook = async () => {
    if (!editableRulebook.gameName.trim()) {
      showToast("游戏名称不能为空。", "error");
      return;
    }
    const updatedGameData = { ...gameData, rulebook: { ...editableRulebook } };
    setGameData(updatedGameData);
    await saveGame(updatedGameData);
    showToast("游戏规则/故事已更新并保存。", "success");
    setShowEditRulebookModal(false);
  };
  
  const getAiContextForDisplay = () => {
    if (currentTurnIndex < 0 || !currentTurn || !currentTurn.promptsSentToAi) {
      return "游戏尚未开始或当前回合无上下文记录...";
    }
  
    const prompts = currentTurn.promptsSentToAi;
  
    if (prompts.functionCalling) {
      return `AI 调用模式: 函数调用模式 (实验性)\n---\n${prompts.functionCalling}`;
    }
  
    if (Object.keys(prompts).length > 0) {
      let context = `AI 调用模式: ${settings.aiCallMode.charAt(0).toUpperCase() + settings.aiCallMode.slice(1)} 模式\n---\n`;
      let step = 1;

      if(prompts.calculationHelper){
         context += `\n### 步骤 ${step++}: 规划AI (Calculation Helper) - 实际输入\n\`\`\`text\n${prompts.calculationHelper}\n\`\`\`\n`;
      }
      if(prompts.narrative){
         context += `\n### 步骤 ${step++}: ${settings.aiCallMode === 'single' ? '整合调用' : '叙事/数据调用'} - 实际输入\n\`\`\`text\n${prompts.narrative}\n\`\`\`\n`;
      }
      if(prompts.structuredData){
         context += `\n### 步骤 ${step++}: 状态/数据调用 - 实际输入\n\`\`\`text\n${prompts.structuredData}\n\`\`\`\n`;
      }
      return context.trim();
    }
  
    return "当前回合无详细上下文记录。";
  };

  const handleCopyImageQuery = () => {
    if (currentTurn?.imageQuery) {
        navigator.clipboard.writeText(currentTurn.imageQuery)
            .then(() => showToast("提示词已复制！", "success"))
            .catch(err => showToast("复制失败: " + err, "error"));
    }
  };
  
  const handleToggleCalculationHelper = async () => {
    if (settings.aiCallMode !== AiCallMode.Hybrid) {
        showToast("此功能仅在混合模式下可用。", "error");
        return;
    }
    const newState = !settings.enableCalculationHelperAi;
    const newSettings = { ...settings, enableCalculationHelperAi: newState };
    if (newState) {
        newSettings.enableFunctionCallingMode = false; // Mutually exclusive
    }
    onSettingsChange(newSettings);
    await saveSettings(newSettings);
    showToast(`计算助手AI已${newState ? '启用' : '禁用'}。`, "success");
  };


  // --- Render logic ---
  const loadingText = currentAiTask || (gameData.gameHistory.length === 0 ? "加载初始游戏状态..." : "AI 处理中...");
  if (gameData.gameHistory.length === 0 && isLoading) return (<div className="min-h-screen flex flex-col items-center justify-center p-4 bg-gray-900 text-gray-100"><LoadingSpinner text={loadingText} /><button onClick={() => setCurrentScreen(AppScreen.MainMenu)} className="mt-4 px-4 py-2 bg-blue-600 hover-bg-blue-700 rounded text-white">返回主菜单</button></div>);
  if (!currentTurn && !isLoading && gameData.gameHistory.length > 0) return (<div className="min-h-screen flex flex-col items-center justify-center p-4 bg-gray-900 text-gray-100"><p className="text-yellow-500">准备数据...</p><button onClick={() => setCurrentScreen(AppScreen.MainMenu)} className="mt-4 px-4 py-2 bg-blue-600 hover-bg-blue-700 rounded text-white">返回主菜单</button></div>);

  const defaultImageModelExists = !!settings.defaultNarrativeImageModelId && settings.modelConfigs.some(m => m.id === settings.defaultNarrativeImageModelId);

  return (
    <div className="h-screen flex flex-col p-2 sm:p-4 bg-gray-900 text-gray-100 overflow-hidden">
      <GameHeader
        gameData={gameData}
        isLoading={isLoading}
        currentTurn={currentTurn}
        onShowRulebook={() => { setEditableRulebook({ ...gameData.rulebook }); setShowEditRulebookModal(true); }}
        onShowAiConfig={() => { setEditablePrompts({ ...gameData.aiPrompts }); setShowEditPromptsModal(true); }}
        onShowAiContext={() => setShowAiContextModal(true)}
        onCopyImageQuery={handleCopyImageQuery}
        onConsolidateKeyInfo={handleConsolidateKeyInfo}
        onBacktrack={handleBacktrack}
        onSaveGame={handleSaveGame}
        onExportGame={handleExportGame}
        onExportTxt={() => setShowExportTxtModal(true)}
        onGoToMainMenu={() => setCurrentScreen(AppScreen.MainMenu)}
        showFunctionCallDebugButton={settings.enableFunctionCallingMode && !!settings.showFunctionCallDebug}
        onShowFunctionCallDebug={() => setIsFunctionCallDebugModalOpen(true)}
        onGoToSettings={onGoToSettings}
        isHybridMode={settings.aiCallMode === AiCallMode.Hybrid}
        isCalculationHelperEnabled={!!settings.enableCalculationHelperAi}
        onToggleCalculationHelper={handleToggleCalculationHelper}
      />

      <div ref={containerRef} className="flex-grow flex flex-col lg:flex-row gap-2 sm:gap-4 overflow-hidden">
        <LeftPanel isOpen={isLeftPanelOpen} width={leftPanelWidth} currentTurn={currentTurn} currentTurnIndex={currentTurnIndex} updateDynamicStateField={updateDynamicStateField} changedPaths={changedPaths} aiCallMode={settings.aiCallMode} />
        {isLargeScreen && isLeftPanelOpen && <div onMouseDown={(e) => handleMouseDown(e, 'left')} className="w-1.5 cursor-col-resize flex-shrink-0 bg-gray-600 hover:bg-blue-600 rounded-lg transition-colors" title="调整面板宽度" />}
        
        <div className="w-full lg:flex-grow flex flex-col overflow-hidden relative">
          <button onClick={() => setIsLeftPanelOpen(p => !p)} className="absolute left-[-8px] top-1/2 -translate-y-1/2 z-20 bg-gray-700/60 hover:bg-blue-600 text-white px-0.5 py-4 rounded-r-lg hidden lg:block backdrop-blur-sm" title={isLeftPanelOpen ? "折叠左侧面板" : "展开左侧面板"}>{isLeftPanelOpen ? <ChevronDoubleLeftIcon className="w-4 h-4" /> : <ChevronDoubleRightIcon className="w-4 h-4" />}</button>
          <button onClick={() => setIsRightPanelOpen(p => !p)} className="absolute right-[-8px] top-1/2 -translate-y-1/2 z-20 bg-gray-700/60 hover:bg-blue-600 text-white px-0.5 py-4 rounded-l-lg hidden lg:block backdrop-blur-sm" title={isRightPanelOpen ? "折叠右侧面板" : "展开右侧面板"}>{isRightPanelOpen ? <ChevronDoubleRightIcon className="w-4 h-4" /> : <ChevronDoubleLeftIcon className="w-4 h-4" />}</button>
          <CenterPanel {...{ isImageLoading, currentAiTask, currentImage, currentTurn, gameHistory: gameData.gameHistory, isLoading, customAction, defaultImageModelExists, mainContentRef, setCustomAction, handleOptionClick, handleCustomActionSubmit, updateField }} />
        </div>

        {isLargeScreen && isRightPanelOpen && <div onMouseDown={(e) => handleMouseDown(e, 'right')} className="w-1.5 cursor-col-resize flex-shrink-0 bg-gray-600 hover:bg-blue-600 rounded-lg transition-colors" title="调整面板宽度" />}
        <RightPanel isOpen={isRightPanelOpen} width={rightPanelWidth} gameHistory={gameData.gameHistory} currentTurnIndex={currentTurnIndex} handleTurnKeyInfoSave={handleTurnKeyInfoSave} />
      </div>
      
      {/* Modals */}
      <ManualParseModal isOpen={manualParseInfo.isOpen} rawText={manualParseInfo.rawText} onContinue={manualParseInfo.onContinue} onCancel={manualParseInfo.onCancel} showToast={showToast} />
      {/* FIX: Pass missing props gameData, settings, and showToast to RulebookModal */}
      <RulebookModal isOpen={showEditRulebookModal} onClose={() => setShowEditRulebookModal(false)} gameData={gameData} settings={settings} editableRulebook={editableRulebook} handleRulebookEdit={(field, value) => setEditableRulebook(p => ({ ...p, [field]: value }))} saveEditedRulebook={saveEditedRulebook} showToast={showToast} />
      {/* FIX: Pass all required props to AiConfigModal */}
      <AiConfigModal isOpen={showEditPromptsModal} onClose={() => setShowEditPromptsModal(false)} gameData={gameData} settings={settings} editablePrompts={editablePrompts} editableMacros={editableMacros} editableChatHistory={editableChatHistory} setEditablePrompts={setEditablePrompts} setEditableMacros={setEditableMacros} setEditableChatHistory={setEditableChatHistory} onSave={saveEditedPromptsAndMacros} onShowHelp={(content) => { setHelpModalContent(content); setIsHelpModalOpen(true); }} nativeFunctionsDoc={NATIVE_FUNCTIONS_DOCUMENTATION} macroTutorial={MACRO_TUTORIAL_CONTENT} showToast={showToast} />
      <HelpModal isOpen={isHelpModalOpen} onClose={() => setIsHelpModalOpen(false)} content={helpModalContent} />
      <ExportTxtModal isOpen={showExportTxtModal} onClose={() => setShowExportTxtModal(false)} handleExportTxt={handleExportTxt} handleExportStatusTxt={handleExportStatusTxt} />
      <ConsolidateKeyInfoModal isOpen={isConsolidateModalOpen} onClose={() => setIsConsolidateModalOpen(false)} consolidationSummary={consolidationSummary} setConsolidationSummary={setConsolidationSummary} confirmAndApplyConsolidation={confirmAndApplyConsolidation} />
      <CalculationDebugModal debugInfo={calculationDebugInfo} onClose={() => setCalculationDebugInfo(null)} />

      {showAiContextModal && (
            <Modal isOpen={showAiContextModal} onClose={() => setShowAiContextModal(false)} title="AI 上下文预览">
                <pre className="text-xs bg-gray-900 p-2 rounded overflow-x-auto max-h-[60vh] whitespace-pre-wrap break-all">
                    {getAiContextForDisplay()}
                </pre>
            </Modal>
        )}
      
      {isFunctionCallDebugModalOpen && (
        <Modal
          isOpen={isFunctionCallDebugModalOpen}
          onClose={() => setIsFunctionCallDebugModalOpen(false)}
          title={`回合 ${currentTurnIndex + 1} - 函数调用日志`}
        >
          <div className="space-y-4 max-h-[60vh] overflow-y-auto">
            {!currentTurn?.functionCallHistory || currentTurn.functionCallHistory.length === 0 ? (
              <p className="text-gray-400">本回合没有记录到函数调用。</p>
            ) : (
              currentTurn.functionCallHistory.map((call, index) => (
                <div key={index} className="p-3 bg-gray-900/50 rounded-lg text-xs">
                  <p className="font-semibold text-cyan-400 font-mono">
                    <span className="text-gray-500">{index + 1}. </span>
                    {call.name}
                  </p>
                  <div className="mt-2">
                    <p className="text-gray-400 font-medium">参数 (Args):</p>
                    <pre className="mt-1 p-2 bg-gray-800 rounded overflow-x-auto whitespace-pre-wrap break-all">
                      {JSON.stringify(call.args, null, 2)}
                    </pre>
                  </div>
                  <div className="mt-2">
                    <p className="text-gray-400 font-medium">结果 (Result):</p>
                    <pre className="mt-1 p-2 bg-gray-800 rounded overflow-x-auto whitespace-pre-wrap break-all">
                      {call.result}
                    </pre>
                  </div>
                </div>
              ))
            )}
          </div>
        </Modal>
      )}
    </div>
  );
};

export default GameScreen;